#include "qterminal.h"
#include <QProcess>
#include <QDebug>
#include <QDir>
#include <QTime>
#include <QFile>
#include <QTextStream>
#include "logger.h"

class QTerminalPrivate : public QObject
{
    Q_OBJECT
public:
    QTerminalPrivate(const QString &password) : m_password(password) {}
    bool run(const QString &cmd, int timeout = -1, QString *pmessage = Q_NULLPTR);
    bool run(const QString &cmd, bool asAdmin = false, int timeout = -1, QString *pmessage = Q_NULLPTR);
    bool run(const QStringList &cmdlist, bool asAdmin = false, int timeout = -1, QString *pmessage = Q_NULLPTR);

    int random(int min, int max);

private:
    QString m_password;
};

int QTerminalPrivate::random(int min, int max)
{
    qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
    int num = qrand() % (max - min);
    return num + min;
}

bool QTerminalPrivate::run(const QString &cmd, int msec, QString *pmessage)
{
    bool success;
    QString message;
    QProcess process;
    connect(&process, &QProcess::readyReadStandardOutput, this, [&] {
        QString output = process.readAllStandardOutput();
        sysDebug("etta@Ubuntu:~$: Output: %s", output.toStdString().data());
        bool ok;
        int ret = output.toInt(&ok);
        if (ok)
        {
            success = (ret == 0);
            process.close();
        }
        else if (output.length() > 1)
        {
            message += (message.length() > 0) ? "\n" : "";
            message += output.mid(0, output.length() - 1);
        }
    });

    connect(&process, &QProcess::readyReadStandardError, this, [&] {
        QString error = process.readAllStandardError();
        sysDebug("etta@Ubuntu:~$: Error: %s", error.toStdString().data());
        if (error.length() > 1)
        {
            message += (error.length() > 0) ? "\n" : "";
            message += error.mid(0, error.length() - 1);
        }
    });

    process.start("bash");
    process.waitForStarted();
    success = true;

    sysDebug("etta@Ubuntu:~$: %s", cmd.toStdString().data());
    process.write(cmd.toLocal8Bit() + "\n");
    process.write("echo $?\n");
    process.waitForFinished(msec);
    if (pmessage)
    {
        *pmessage = message;
    }

    return success;
}

bool QTerminalPrivate::run(const QString &cmd, bool asAdmin, int timeout, QString *pmessage)
{
    return run(QString("%1 %2").arg(asAdmin ? QString("echo %1 | sudo -S").arg(m_password) : "").arg(cmd), timeout, pmessage);
}

bool QTerminalPrivate::run(const QStringList &cmdlist, bool asAdmin, int timeout, QString *pmessage)
{
    QString fname = QDir::temp().absoluteFilePath(QString("qterminal_%1.sh").arg(random(1, 9999)));

    QFile file(fname);
    if (file.open(QFile::WriteOnly | QFile::Truncate))
    {
        QTextStream dataStream(&file);
        foreach (const QString &cmd, cmdlist)
        {
            dataStream << cmd << "\n";
        }

        file.close();
    }

    QString message;

    bool ret = true;
    ret = ret && run(QString("bash %1\n").arg(fname), asAdmin, timeout, &message);

    if (pmessage)
    {
        *pmessage = message;
    }

    QFile::remove(fname);
    return ret;
}

QTerminal::QTerminal() : d(new QTerminalPrivate("")) {}

QTerminal::QTerminal(const QString &password) : d(new QTerminalPrivate(password)) {}

QTerminal::~QTerminal() { delete d; }

bool QTerminal::run(const QString &cmd, QString *message)
{
    return d->run(cmd, -1, message);
}

bool QTerminal::run(const QStringList &cmdlist, QString *message)
{
    return d->run(cmdlist, false, -1, message);
}

bool QTerminal::run(const QString &cmd, bool asAdmin, QString *message)
{
    return d->run(cmd, asAdmin, -1, message);
}

bool QTerminal::run(const QStringList &cmdlist, bool asAdmin, QString *message)
{
    return d->run(cmdlist, asAdmin, -1, message);
}

bool QTerminal::runAsAdmin(const QString &cmd, QString *message)
{
    return d->run(cmd, true, -1, message);
}

bool QTerminal::runAsAdmin(const QStringList &cmdlist, QString *message)
{
    return d->run(cmdlist, true, -1, message);
}


#include "qterminal.moc"
